
package xifileserver;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.concurrent.Semaphore;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * It's the server which receive all user connection and give the task to a new thread
 * @version 0.1
 * @author saluzzo
 */
public class UserConnectionServer extends Thread  {

    /**
     * Object of singleton UserConnectionServer
     */
    static private UserConnectionServer InstanceOfSingleton = null;

    /**
     * This is the handle of Socket Server used to connect a client to the application
     */
    private ServerSocket Server;

    /**
     * The list of the user connected in the application
     */
    private ArrayList<UserThread> UserThreadList;

    /**
     * The TCP port connection
     */
    private int Port;

    /**
     * The maximum numbers of user should be connected to the application
     */
    private int MaxUser;

    /**
     * the boolean used by the thread to know if you should run or no
     */
    private boolean running;

    /**
     * Semaphore used by the thread to block him
     */
    private Semaphore BlockedThread;
    /**
     * UserConnectionServer constructor
     * <b>this constructar are set private because this class is a singleton.<br/>
     * To consrtuct a UserConnectionServer, use the static method @see construct
     * </b>
     * @param port TCP port of the connection
     * @param maxUser the number of maximum user
     */
    private UserConnectionServer(int port, int maxUser) {
        try {
            Server = new ServerSocket(port);
        } catch (IOException ex) {
            Logger.getLogger(UserConnectionServer.class.getName()).log(Level.SEVERE, null, ex);
        }
        UserThreadList = new ArrayList<UserThread>();
        this.Port = port;
        this.MaxUser = maxUser;
        this.running = true;
        this.BlockedThread = new Semaphore(1);
    }

    /**
     * the usable method to construct an UserConnectionServer Object, this method was created
     * to used in the singleton method
     * @param port TCP port of the connection
     * @param maxUser the number of maximum user
     * @return the Singleton UserConnectionServer object
     */
    public static UserConnectionServer construct(int port, int maxUser) {
        if(UserConnectionServer.InstanceOfSingleton == null) {
            UserConnectionServer.InstanceOfSingleton = new UserConnectionServer(port, maxUser);
            return UserConnectionServer.InstanceOfSingleton;
        } else {
            return UserConnectionServer.InstanceOfSingleton;
        }
    }
    
    /**
     * This method are override to decide the process of the Thread
     * @Override
     */
    @Override
    public void run() {
        super.run();
        while(true) {
            try {
                this.BlockedThread.acquire();
            } catch (InterruptedException ex) {
                Logger.getLogger(UserConnectionServer.class.getName()).log(Level.SEVERE, null, ex);
            }
            while(running) {
               //Process du thread
                Socket newUser = this.waitUserConnection();

            }
        }
        
        //Thread while
    }
    /**
     * Stop the thread and interrupt all the process to deblock a possible wait by a function
     */
    public void stopp() {
        this.running = false;
        this.interrupt();
    }

    /**
     * Used to restart the thread
     */
    public void restart() {
        this.running = true;
        this.BlockedThread.release();
    }

    /**
     * this function wait a connection by an user to the server.
     * If the max user limit isn't attempt, the socket handle of user are returned
     * else the user are informed and disconnect immediatly and the function return null
     * @return the socket handle of user OR null if the max user limit is attempt
     */
    private Socket waitUserConnection() {
        Socket User = null;
        try {
            User = Server.accept();
        } catch (IOException ex) {
            Logger.getLogger(UserConnectionServer.class.getName()).log(Level.SEVERE, null, ex);
        }
        if(this.UserThreadList.size() < this.MaxUser) {
            //the application havn't attempt the max user limit
            return User;
        } else {
            //the numbers of users are equal to the limit, connection refused
            try {
                PrintWriter pled = null;
                pled = new PrintWriter(new BufferedWriter(new OutputStreamWriter(User.getOutputStream())), true);
                pled.println(CommunicationProtocol.errMaxUserLimitAttempt);
                User.close();
            } catch (IOException ex) {
                Logger.getLogger(UserConnectionServer.class.getName()).log(Level.SEVERE, null, ex);
            }
            return null;
        }
    }
}
